
#ifndef GZ_SUDOKU_JCZSOLVE_V1_H
#define GZ_SUDOKU_JCZSOLVE_V1_H

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <memory.h>
#include <assert.h>

#include <cstdint>
#include <cstddef>
#include <cstring>      // For std::memset(), std::memcpy()
#include <vector>
#include <bitset>
#include <array>        // For std::array<T, Size>

#include "BasicSolver.h"
#include "Sudoku.h"
#include "SudokuTable.h"
#include "StopWatch.h"
#include "BitUtils.h"
#include "BitSet.h"
#include "PackedBitSet.h"
#include "BitArray.h"
#include "BitVec.h"

//
// Whether use R1 counter and compare ? Disable it maybe faster.
//
#define JCZ_V1_ENABLE_R1_COUNT      0

//
// Whether use 64 bit band struct?
//
#define JCZ_V1_USE_64BIT_BANDS      1

//
// Whether search no guess steps only?
//
#define JCZ_V1_ONLY_NO_GUESS        0

namespace gzSudoku {
namespace JCZ {
namespace v1 {

static const size_t kSearchMode = SearchMode::OneSolution;

static const bool kCheckSolvedRows = false;
static const bool kUseFastMode = false;

// Kill all in other blocks locked column / box
static const uint32_t colLockedSingleMaskTbl[512] = {
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07767767767, 07766766766, 07765765765, 07767767767, 07763763763, 07767767767, 07767767767, 07767767767,
    07757757757, 07756756756, 07755755755, 07757757757, 07753753753, 07757757757, 07757757757, 07757757757,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07737737737, 07736736736, 07735735735, 07737737737, 07733733733, 07737737737, 07737737737, 07737737737,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07677677677, 07676676676, 07675675675, 07677677677, 07673673673, 07677677677, 07677677677, 07677677677,
    07667667667, 07666666666, 07665665665, 07667667667, 07663663663, 07667667667, 07667667667, 07667667667,
    07657657657, 07656656656, 07655655655, 07657657657, 07653653653, 07657657657, 07657657657, 07657657657,
    07677677677, 07676676676, 07675675675, 07677677677, 07673673673, 07677677677, 07677677677, 07677677677,
    07637637637, 07636636636, 07635635635, 07637637637, 07633633633, 07637637637, 07637637637, 07637637637,
    07677677677, 07676676676, 07675675675, 07677677677, 07673673673, 07677677677, 07677677677, 07677677677,
    07677677677, 07676676676, 07675675675, 07677677677, 07673673673, 07677677677, 07677677677, 07677677677,
    07677677677, 07676676676, 07675675675, 07677677677, 07673673673, 07677677677, 07677677677, 07677677677,
    07577577577, 07576576576, 07575575575, 07577577577, 07573573573, 07577577577, 07577577577, 07577577577,
    07567567567, 07566566566, 07565565565, 07567567567, 07563563563, 07567567567, 07567567567, 07567567567,
    07557557557, 07556556556, 07555555555, 07557557557, 07553553553, 07557557557, 07557557557, 07557557557,
    07577577577, 07576576576, 07575575575, 07577577577, 07573573573, 07577577577, 07577577577, 07577577577,
    07537537537, 07536536536, 07535535535, 07537537537, 07533533533, 07537537537, 07537537537, 07537537537,
    07577577577, 07576576576, 07575575575, 07577577577, 07573573573, 07577577577, 07577577577, 07577577577,
    07577577577, 07576576576, 07575575575, 07577577577, 07573573573, 07577577577, 07577577577, 07577577577,
    07577577577, 07576576576, 07575575575, 07577577577, 07573573573, 07577577577, 07577577577, 07577577577,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07767767767, 07766766766, 07765765765, 07767767767, 07763763763, 07767767767, 07767767767, 07767767767,
    07757757757, 07756756756, 07755755755, 07757757757, 07753753753, 07757757757, 07757757757, 07757757757,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07737737737, 07736736736, 07735735735, 07737737737, 07733733733, 07737737737, 07737737737, 07737737737,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07377377377, 07376376376, 07375375375, 07377377377, 07373373373, 07377377377, 07377377377, 07377377377,
    07367367367, 07366366366, 07365365365, 07367367367, 07363363363, 07367367367, 07367367367, 07367367367,
    07357357357, 07356356356, 07355355355, 07357357357, 07353353353, 07357357357, 07357357357, 07357357357,
    07377377377, 07376376376, 07375375375, 07377377377, 07373373373, 07377377377, 07377377377, 07377377377,
    07337337337, 07336336336, 07335335335, 07337337337, 07333333333, 07337337337, 07337337337, 07337337337,
    07377377377, 07376376376, 07375375375, 07377377377, 07373373373, 07377377377, 07377377377, 07377377377,
    07377377377, 07376376376, 07375375375, 07377377377, 07373373373, 07377377377, 07377377377, 07377377377,
    07377377377, 07376376376, 07375375375, 07377377377, 07373373373, 07377377377, 07377377377, 07377377377,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07767767767, 07766766766, 07765765765, 07767767767, 07763763763, 07767767767, 07767767767, 07767767767,
    07757757757, 07756756756, 07755755755, 07757757757, 07753753753, 07757757757, 07757757757, 07757757757,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07737737737, 07736736736, 07735735735, 07737737737, 07733733733, 07737737737, 07737737737, 07737737737,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07767767767, 07766766766, 07765765765, 07767767767, 07763763763, 07767767767, 07767767767, 07767767767,
    07757757757, 07756756756, 07755755755, 07757757757, 07753753753, 07757757757, 07757757757, 07757757757,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07737737737, 07736736736, 07735735735, 07737737737, 07733733733, 07737737737, 07737737737, 07737737737,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07767767767, 07766766766, 07765765765, 07767767767, 07763763763, 07767767767, 07767767767, 07767767767,
    07757757757, 07756756756, 07755755755, 07757757757, 07753753753, 07757757757, 07757757757, 07757757757,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07737737737, 07736736736, 07735735735, 07737737737, 07733733733, 07737737737, 07737737737, 07737737737,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777,
    07777777777, 07776776776, 07775775775, 07777777777, 07773773773, 07777777777, 07777777777, 07777777777
};

// Triads mask per row
static const uint32_t rowTriadsMaskTbl[512] = {
    0, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3,
    2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7
};

// Keep all locked candidates
static const uint32_t keepLockedCandidatesTbl[512] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 07007070700, 07707070700, 07007770700, 07707770700,
    0, 0, 0, 0, 07077070700, 07777070700, 07777770700, 07777770700,
    0, 0, 07007700070, 07077700070, 0, 0, 07007770070, 07077770070,
    0, 0, 07707700070, 07777700070, 0, 0, 07777770070, 07777770070,
    0, 0, 07007700770, 07777700770, 07007070770, 07777070770, 07007770770, 07777770770,
    0, 0, 07707700770, 07777700770, 07077070770, 07777070770, 07777770770, 07777770770,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 07070007700, 07070707700, 07770007700, 07770707700,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 07077007700, 07777707700, 07777007700, 07777707700,
    0, 07070700007, 0, 07077700007, 0, 07070707007, 0, 07077707007,
    0, 07070700707, 0, 07777700707, 07070007707, 07070707707, 07777007707, 07777707707,
    0, 07770700007, 0, 07777700007, 0, 07777707007, 0, 07777707007,
    0, 07770700707, 0, 07777700707, 07077007707, 07777707707, 07777007707, 07777707707,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 07070077700, 07070777700, 07770777700, 07770777700,
    0, 0, 0, 0, 07007077700, 07707777700, 07007777700, 07707777700,
    0, 0, 0, 0, 07077077700, 07777777700, 07777777700, 07777777700,
    0, 07070700077, 07007700077, 07077700077, 0, 07070777077, 07007777077, 07077777077,
    0, 07070700777, 07707700777, 07777700777, 07070077777, 07070777777, 07777777777, 07777777777,
    0, 07770700777, 07007700777, 07777700777, 07007077777, 07777777777, 07007777777, 07777777777,
    0, 07770700777, 07707700777, 07777700777, 07077077777, 07777777777, 07777777777, 07777777777,
    0, 0, 0, 0, 0, 0, 0, 0,
    00, 0, 07700007070, 07700077070, 0, 0, 07770007070, 07770077070,
    00, 07700070007, 0, 07700077007, 0, 07707070007, 0, 07707077007,
    00, 07700070077, 07700007077, 07700077077, 0, 07777070077, 07777007077, 07777077077,
    00, 0, 0, 0, 0, 0, 0, 0,
    00, 0, 07707007070, 07777077070, 0, 0, 07777007070, 07777077070,
    00, 07770070007, 0, 07777077007, 0, 07777070007, 0, 07777077007,
    00, 07770070077, 07707007077, 07777077077, 0, 07777070077, 07777007077, 07777077077,
    00, 0, 0, 0, 0, 0, 0, 0,
    00, 0, 07700707070, 07700777070, 0, 0, 07770777070, 07770777070,
    00, 07700070707, 0, 07700777707, 07007070707, 07707070707, 07007777707, 07707777707,
    00, 07700070777, 07700707777, 07700777777, 07077070777, 07777070777, 07777777777, 07777777777,
    00, 0, 07007707070, 07077777070, 0, 0, 07007777070, 07077777070,
    00, 0, 07707707070, 07777777070, 0, 0, 07777777070, 07777777070,
    00, 07770070777, 07007707777, 07777777777, 07007070777, 07777070777, 07007777777, 07777777777,
    00, 07770070777, 07707707777, 07777777777, 07077070777, 07777070777, 07777777777, 07777777777,
    00, 0, 0, 0, 0, 0, 0, 0,
    00, 0, 07700007770, 07700777770, 07070007770, 07070777770, 07770007770, 07770777770,
    00, 07700770007, 0, 07700777007, 0, 07707777007, 0, 07707777007,
    00, 07700770777, 07700007777, 07700777777, 07077007777, 07777777777, 07777007777, 07777777777,
    00, 07070770007, 0, 07077777007, 0, 07070777007, 0, 07077777007,
    00, 07070770777, 07707007777, 07777777777, 07070007777, 07070777777, 07777007777, 07777777777,
    00, 07770770007, 0, 07777777007, 0, 07777777007, 0, 07777777007,
    00, 07770770777, 07707007777, 07777777777, 07077007777, 07777777777, 07777007777, 07777777777,
    00, 0, 0, 0, 0, 0, 0, 0,
    00, 0, 07700707770, 07700777770, 07070077770, 07070777770, 07770777770, 07770777770,
    00, 07700770707, 0, 07700777707, 07007077707, 07707777707, 07007777707, 07707777707,
    00, 07700770777, 07700707777, 07700777777, 07077077777, 07777777777, 07777777777, 07777777777,
    00, 07070770077, 07007707077, 07077777077, 0, 07070777077, 07007777077, 07077777077,
    00, 07070770777, 07707707777, 07777777777, 07070077777, 07070777777, 07777777777, 07777777777,
    00, 07770770777, 07007707777, 07777777777, 07007077777, 07777777777, 07007777777, 07777777777,
    00, 07770770777, 07707707777, 07777777777, 07077077777, 07777777777, 07777777777, 07777777777
};

// Get the masks of all row triads is single
static const uint32_t rowTriadsSingleMaskTbl[512] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0124, 0124, 0124, 0124, 0, 0, 0, 0, 0124, 0124, 0124, 0124,
    0, 0, 0142, 0142, 0, 0, 0142, 0142, 0, 0, 0142, 0142, 0, 0, 0142, 0142, 0, 0, 0142, 0142, 0124, 0124, 0100, 0100, 0, 0, 0142, 0142, 0124, 0124, 0100, 0100,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0214, 0214, 0214, 0214, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0214, 0214, 0214, 0214,
    0, 0241, 0, 0241, 0, 0241, 0, 0241, 0, 0241, 0, 0241, 0214, 0200, 0214, 0200, 0, 0241, 0, 0241, 0, 0241, 0, 0241, 0, 0241, 0, 0241, 0214, 0200, 0214, 0200,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0214, 0214, 0214, 0214, 0, 0, 0, 0, 0124, 0124, 0124, 0124, 0, 0, 0, 0, 04, 04, 04, 04,
    0, 0241, 0142, 040, 0, 0241, 0142, 040, 0, 0241, 0142, 040, 0214, 0200, 0, 0, 0, 0241, 0142, 040, 0124, 0, 0100, 0, 0, 0241, 0142, 040, 04, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0412, 0412, 0, 0, 0412, 0412, 0, 0421, 0, 0421, 0, 0421, 0, 0421, 0, 0421, 0412, 0400, 0, 0421, 0412, 0400,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0412, 0412, 0, 0, 0412, 0412, 0, 0421, 0, 0421, 0, 0421, 0, 0421, 0, 0421, 0412, 0400, 0, 0421, 0412, 0400,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0412, 0412, 0, 0, 0412, 0412, 0, 0421, 0, 0421, 0124, 020, 0124, 020, 0, 0421, 0412, 0400, 0124, 020, 0, 0,
    0, 0, 0142, 0142, 0, 0, 0142, 0142, 0, 0, 02, 02, 0, 0, 02, 02, 0, 0421, 0142, 0, 0124, 020, 0100, 0, 0, 0421, 02, 0, 0124, 020, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0412, 0412, 0214, 0214, 010, 010, 0, 0421, 0, 0421, 0, 0421, 0, 0421, 0, 0421, 0412, 0400, 0214, 0, 010, 0,
    0, 0241, 0, 0241, 0, 0241, 0, 0241, 0, 0241, 0412, 0, 0214, 0200, 010, 0, 0, 01, 0, 01, 0, 01, 0, 01, 0, 01, 0412, 0, 0214, 0, 010, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0412, 0412, 0214, 0214, 010, 010, 0, 0421, 0, 0421, 0124, 020, 0124, 020, 0, 0421, 0412, 0400, 04, 0, 0, 0,
    0, 0241, 0142, 040, 0, 0241, 0142, 040, 0, 0241, 02, 0, 0214, 0200, 0, 0, 0, 01, 0142, 0, 0124, 0, 0100, 0, 0, 01, 02, 0, 04, 0, 0, 0
};

// Get the masks of combine all columns in band is single
static const uint32_t combColumnSingleMaskTbl[512] = {
    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
    00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
    00, 00, 00, 00, 00, 00, 00, 00, 00, 0777, 0777, 0666, 0777, 0666, 0666, 0666,
    00, 0777, 0777, 0666, 0777, 0666, 0666, 0666, 00, 0555, 0555, 0444, 0555, 0444, 0444, 0444,
    00, 0777, 0777, 0666, 0777, 0666, 0666, 0666, 00, 0555, 0555, 0444, 0555, 0444, 0444, 0444,
    00, 0555, 0555, 0444, 0555, 0444, 0444, 0444, 00, 0555, 0555, 0444, 0555, 0444, 0444, 0444,
    00, 00, 00, 00, 00, 00, 00, 00, 00, 0777, 0777, 0666, 0777, 0666, 0666, 0666,
    00, 0777, 0777, 0666, 0777, 0666, 0666, 0666, 00, 0555, 0555, 0444, 0555, 0444, 0444, 0444,
    00, 0777, 0777, 0666, 0777, 0666, 0666, 0666, 00, 0555, 0555, 0444, 0555, 0444, 0444, 0444,
    00, 0555, 0555, 0444, 0555, 0444, 0444, 0444, 00, 0555, 0555, 0444, 0555, 0444, 0444, 0444,
    00, 00, 00, 00, 00, 00, 00, 00, 00, 0333, 0333, 0222, 0333, 0222, 0222, 0222,
    00, 0333, 0333, 0222, 0333, 0222, 0222, 0222, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 0333, 0333, 0222, 0333, 0222, 0222, 0222, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 0111, 0111, 00, 0111, 00, 00, 00, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 00, 00, 00, 00, 00, 00, 00, 00, 0777, 0777, 0666, 0777, 0666, 0666, 0666,
    00, 0777, 0777, 0666, 0777, 0666, 0666, 0666, 00, 0555, 0555, 0444, 0555, 0444, 0444, 0444,
    00, 0777, 0777, 0666, 0777, 0666, 0666, 0666, 00, 0555, 0555, 0444, 0555, 0444, 0444, 0444,
    00, 0555, 0555, 0444, 0555, 0444, 0444, 0444, 00, 0555, 0555, 0444, 0555, 0444, 0444, 0444,
    00, 00, 00, 00, 00, 00, 00, 00, 00, 0333, 0333, 0222, 0333, 0222, 0222, 0222,
    00, 0333, 0333, 0222, 0333, 0222, 0222, 0222, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 0333, 0333, 0222, 0333, 0222, 0222, 0222, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 0111, 0111, 00, 0111, 00, 00, 00, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 00, 00, 00, 00, 00, 00, 00, 00, 0333, 0333, 0222, 0333, 0222, 0222, 0222,
    00, 0333, 0333, 0222, 0333, 0222, 0222, 0222, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 0333, 0333, 0222, 0333, 0222, 0222, 0222, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 0111, 0111, 00, 0111, 00, 00, 00, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 00, 00, 00, 00, 00, 00, 00, 00, 0333, 0333, 0222, 0333, 0222, 0222, 0222,
    00, 0333, 0333, 0222, 0333, 0222, 0222, 0222, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 0333, 0333, 0222, 0333, 0222, 0222, 0222, 00, 0111, 0111, 00, 0111, 00, 00, 00,
    00, 0111, 0111, 00, 0111, 00, 00, 00, 00, 0111, 0111, 00, 0111, 00, 00, 00
};

// Hidden single in row, 1 indicate row is a hidden single, mode: 1 to 111
static const uint32_t rowHiddenSingleMaskTbl[512] = {
    0, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3,
    2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
    6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7,
};

// Hidden single in row, 1 indicate row is not a hidden single, mode: 1 to 111
static const int rowHiddenSingleReverseMaskTbl[512] = {
    7, 6, 6, 6, 6, 6, 6, 6, 5, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4,
    5, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4,
    3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
    1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0
};

// Rows where single found  000 to 111
static const unsigned int solvedRowsBitMaskTbl[8] = {
    00, 0777, 0777000, 0777777, 0777000000, 0777000777, 0777777000, 0777777777
};

// Rows where single found  000 to 111
static const unsigned int solvedRowsReverseBitMaskTbl[8] = {
    0777777777, 0777777000, 0777000777, 0777000000, 0777777, 0777000, 0777, 00
};

static const uint32_t bandUnsolvedMaskTbl32[81] = {
    0x37E3F001, 0x37E3F002, 0x37E3F004, 0x371F8E08, 0x371F8E10, 0x371F8E20, 0x30FC7E40, 0x30FC7E80,
    0x30FC7F00, 0x2FE003F8, 0x2FE005F8, 0x2FE009F8, 0x2F1C11C7, 0x2F1C21C7, 0x2F1C41C7, 0x28FC803F,
    0x28FD003F, 0x28FE003F, 0x1807F1F8, 0x180BF1F8, 0x1813F1F8, 0x18238FC7, 0x18438FC7, 0x18838FC7,
    0x19007E3F, 0x1A007E3F, 0x1C007E3F, 0x37E3F001, 0x37E3F002, 0x37E3F004, 0x371F8E08, 0x371F8E10,
    0x371F8E20, 0x30FC7E40, 0x30FC7E80, 0x30FC7F00, 0x2FE003F8, 0x2FE005F8, 0x2FE009F8, 0x2F1C11C7,
    0x2F1C21C7, 0x2F1C41C7, 0x28FC803F, 0x28FD003F, 0x28FE003F, 0x1807F1F8, 0x180BF1F8, 0x1813F1F8,
    0x18238FC7, 0x18438FC7, 0x18838FC7, 0x19007E3F, 0x1A007E3F, 0x1C007E3F, 0x37E3F001, 0x37E3F002,
    0x37E3F004, 0x371F8E08, 0x371F8E10, 0x371F8E20, 0x30FC7E40, 0x30FC7E80, 0x30FC7F00, 0x2FE003F8,
    0x2FE005F8, 0x2FE009F8, 0x2F1C11C7, 0x2F1C21C7, 0x2F1C41C7, 0x28FC803F, 0x28FD003F, 0x28FE003F,
    0x1807F1F8, 0x180BF1F8, 0x1813F1F8, 0x18238FC7, 0x18438FC7, 0x18838FC7, 0x19007E3F, 0x1A007E3F,
    0x1C007E3F
};

static const uint64_t bandUnsolvedMaskTbl64[81] = {
    0xFFFFFFFF37E3F001, 0xFFFFFFFF37E3F002, 0xFFFFFFFF37E3F004, 0xFFFFFFFF371F8E08,
    0xFFFFFFFF371F8E10, 0xFFFFFFFF371F8E20, 0xFFFFFFFF30FC7E40, 0xFFFFFFFF30FC7E80,

    0xFFFFFFFF30FC7F00, 0xFFFFFFFF2FE003F8, 0xFFFFFFFF2FE005F8, 0xFFFFFFFF2FE009F8,
    0xFFFFFFFF2F1C11C7, 0xFFFFFFFF2F1C21C7, 0xFFFFFFFF2F1C41C7, 0xFFFFFFFF28FC803F,

    0xFFFFFFFF28FD003F, 0xFFFFFFFF28FE003F, 0xFFFFFFFF1807F1F8, 0xFFFFFFFF180BF1F8,
    0xFFFFFFFF1813F1F8, 0xFFFFFFFF18238FC7, 0xFFFFFFFF18438FC7, 0xFFFFFFFF18838FC7,

    0xFFFFFFFF19007E3F, 0xFFFFFFFF1A007E3F, 0xFFFFFFFF1C007E3F, 0x37E3F001FFFFFFFF,  // 28 (27 + 1)
    0x37E3F002FFFFFFFF, 0x37E3F004FFFFFFFF, 0x371F8E08FFFFFFFF, 0x371F8E10FFFFFFFF,

    0x371F8E20FFFFFFFF, 0x30FC7E40FFFFFFFF, 0x30FC7E80FFFFFFFF, 0x30FC7F00FFFFFFFF,
    0x2FE003F8FFFFFFFF, 0x2FE005F8FFFFFFFF, 0x2FE009F8FFFFFFFF, 0x2F1C11C7FFFFFFFF,

    0x2F1C21C7FFFFFFFF, 0x2F1C41C7FFFFFFFF, 0x28FC803FFFFFFFFF, 0x28FD003FFFFFFFFF,
    0x28FE003FFFFFFFFF, 0x1807F1F8FFFFFFFF, 0x180BF1F8FFFFFFFF, 0x1813F1F8FFFFFFFF,

    0x18238FC7FFFFFFFF, 0x18438FC7FFFFFFFF, 0x18838FC7FFFFFFFF, 0x19007E3FFFFFFFFF,
    0x1A007E3FFFFFFFFF, 0x1C007E3FFFFFFFFF, 0xFFFFFFFF37E3F001, 0xFFFFFFFF37E3F002,  // 56 (54 + 2)

    0xFFFFFFFF37E3F004, 0xFFFFFFFF371F8E08, 0xFFFFFFFF371F8E10, 0xFFFFFFFF371F8E20,
    0xFFFFFFFF30FC7E40, 0xFFFFFFFF30FC7E80, 0xFFFFFFFF30FC7F00, 0xFFFFFFFF2FE003F8,

    0xFFFFFFFF2FE005F8, 0xFFFFFFFF2FE009F8, 0xFFFFFFFF2F1C11C7, 0xFFFFFFFF2F1C21C7,
    0xFFFFFFFF2F1C41C7, 0xFFFFFFFF28FC803F, 0xFFFFFFFF28FD003F, 0xFFFFFFFF28FE003F,

    0xFFFFFFFF1807F1F8, 0xFFFFFFFF180BF1F8, 0xFFFFFFFF1813F1F8, 0xFFFFFFFF18238FC7,
    0xFFFFFFFF18438FC7, 0xFFFFFFFF18838FC7, 0xFFFFFFFF19007E3F, 0xFFFFFFFF1A007E3F,

    0xFFFFFFFF1C007E3F
};

static const uint32_t bandPeer1Tbl[4] = { 1, 0, 0, 0 };

static const uint32_t bandPeer2Tbl[4] = { 2, 2, 1, 0 };

static const uint32_t bandOtherUnsolvedMaskTbl[81] = {
    0x3FFBFDFE, 0x3FF7FBFD, 0x3FEFF7FB, 0x3FDFEFF7, 0x3FBFDFEF, 0x3F7FBFDF, 0x3EFF7FBF, 0x3DFEFF7F,
    0x3BFDFEFF, 0x3FFBFDFE, 0x3FF7FBFD, 0x3FEFF7FB, 0x3FDFEFF7, 0x3FBFDFEF, 0x3F7FBFDF, 0x3EFF7FBF,
    0x3DFEFF7F, 0x3BFDFEFF, 0x3FFBFDFE, 0x3FF7FBFD, 0x3FEFF7FB, 0x3FDFEFF7, 0x3FBFDFEF, 0x3F7FBFDF,
    0x3EFF7FBF, 0x3DFEFF7F, 0x3BFDFEFF, 0x3FFBFDFE, 0x3FF7FBFD, 0x3FEFF7FB, 0x3FDFEFF7, 0x3FBFDFEF,
    0x3F7FBFDF, 0x3EFF7FBF, 0x3DFEFF7F, 0x3BFDFEFF, 0x3FFBFDFE, 0x3FF7FBFD, 0x3FEFF7FB, 0x3FDFEFF7,
    0x3FBFDFEF, 0x3F7FBFDF, 0x3EFF7FBF, 0x3DFEFF7F, 0x3BFDFEFF, 0x3FFBFDFE, 0x3FF7FBFD, 0x3FEFF7FB,
    0x3FDFEFF7, 0x3FBFDFEF, 0x3F7FBFDF, 0x3EFF7FBF, 0x3DFEFF7F, 0x3BFDFEFF, 0x3FFBFDFE, 0x3FF7FBFD,
    0x3FEFF7FB, 0x3FDFEFF7, 0x3FBFDFEF, 0x3F7FBFDF, 0x3EFF7FBF, 0x3DFEFF7F, 0x3BFDFEFF, 0x3FFBFDFE,
    0x3FF7FBFD, 0x3FEFF7FB, 0x3FDFEFF7, 0x3FBFDFEF, 0x3F7FBFDF, 0x3EFF7FBF, 0x3DFEFF7F, 0x3BFDFEFF,
    0x3FFBFDFE, 0x3FF7FBFD, 0x3FEFF7FB, 0x3FDFEFF7, 0x3FBFDFEF, 0x3F7FBFDF, 0x3EFF7FBF, 0x3DFEFF7F,
    0x3BFDFEFF,
};

static const int8_t bandBitPosToPos64[2][64] = {
    // bit64[0]
    {
         0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, -1, -1,
        27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
        43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1
    },
        
    // bit64[1]
    {
        54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
        70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
    }
};

static const int8_t bandBitPosToPos32[4][32] = {
    // bit32[0]
    {
         0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, -1, -1, -1, -1, -1
    },

    // bit32[1]
    {
        27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
        43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1
    },
        
    // bit32[2]
    {
        54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
        70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, -1, -1, -1, -1, -1
    },

    // bit32[3]
    {
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
    }
};

class Solver : public BasicSolver {
public:
    typedef BasicSolver                         basic_solver;
    typedef Solver                              this_type;

    typedef typename SudokuTable::NeighborCells NeighborCells;
    typedef typename SudokuTable::CellInfo      CellInfo;
    typedef typename SudokuTable::BoxesInfo     BoxesInfo;

    typedef typename Sudoku::BitMask            BitMask;
    typedef typename Sudoku::BitMaskTable       BitMaskTable;

    static const size_t kAlignment = Sudoku::Alignment;
    static const size_t BoxCellsX = Sudoku::BoxCellsX;      // 3
    static const size_t BoxCellsY = Sudoku::BoxCellsY;      // 3
    static const size_t BoxCountX = Sudoku::BoxCountX;      // 3
    static const size_t BoxCountY = Sudoku::BoxCountY;      // 3
    static const size_t MinNumber = Sudoku::MinNumber;      // 1
    static const size_t MaxNumber = Sudoku::MaxNumber;      // 9

    static const size_t Rows = Sudoku::Rows;
    static const size_t Cols = Sudoku::Cols;
    static const size_t Boxes = Sudoku::Boxes;
    static const size_t BoxSize = Sudoku::BoxSize;
    static const size_t Numbers = Sudoku::Numbers;

    static const size_t BoardSize = Sudoku::BoardSize;
    static const size_t TotalSize = Sudoku::TotalSize;
    static const size_t Neighbors = Sudoku::Neighbors;

    static const size_t Rows16 = Sudoku::Rows16;
    static const size_t Cols16 = Sudoku::Cols16;
    static const size_t Numbers10 = Sudoku::Numbers10;
    static const size_t Numbers16 = Sudoku::Numbers16;
    static const size_t Boxes16 = Sudoku::Boxes16;
    static const size_t BoxSize16 = Sudoku::BoxSize16;
    static const size_t BoardSize16 = Sudoku::BoardSize16;

    static const size_t kAllRowBits = Sudoku::AllRowBits;
    static const size_t kAllColBits = Sudoku::AllColBits;
    static const size_t kAllBoxBits = Sudoku::AllBoxBits;
    static const size_t kAllBoxCellBits = Sudoku::AllBoxCellBits;
    static const size_t kAllNumberBits = Sudoku::AllNumberBits;

    static const bool kAllDimIsSame = Sudoku::AllDimIsSame;

    // all pencil marks set - 27 bits per band
    static const uint32_t kBitSet27          = 0x07FFFFFFUL;
    static const uint64_t kBitSet27_Single64 = 0x0000000007FFFFFFULL;
    static const uint64_t kBitSet27_Double64 = 0x07FFFFFF07FFFFFFULL;

    static const uint32_t kFullRowBits   = 0x01FFUL;
    static const uint32_t kFullRowBits_1 = 0x01FFUL << 9U;
    static const uint32_t kFullRowBits_2 = 0x01FFUL << 18U;

    static const size_t kLimitSolutions = 1;

private:
    enum LiteralType {
        NumRowCols,
        NumColRows,
        NumBoxCells,
        BoxCellNums,
        Unknown
    };

#pragma pack(push, 1)

    union BandBoard {
        uint32_t bands[3];
        struct {
            uint64_t bands64;
            uint32_t bands32;
        };

        void reset() {
#if JCZ_V1_USE_64BIT_BANDS && (defined(WIN64) || defined(_WIN64) || defined(_M_X64) \
 || defined(_M_AMD64) || defined(_M_IA64) || defined(__amd64__) || defined(__x86_64__))
            this->bands64 = 0;
            this->bands32 = 0;
#else
            this->bands[0] = 0;
            this->bands[1] = 0;
            this->bands[2] = 0;
#endif
        }

        void set() {
#if JCZ_V1_USE_64BIT_BANDS && (defined(WIN64) || defined(_WIN64) || defined(_M_X64) \
 || defined(_M_AMD64) || defined(_M_IA64) || defined(__amd64__) || defined(__x86_64__))
            this->bands64 = kBitSet27_Double64;
            this->bands32 = kBitSet27;
#else
            this->bands[0] = kBitSet27;
            this->bands[1] = kBitSet27;
            this->bands[2] = kBitSet27;
#endif
        }
    };

    struct alignas(32) State {
        BandBoard candidates[Numbers];
        BandBoard prevCandidates[Numbers];
        BandBoard solvedCells;
        BandBoard solvedRows;
        BandBoard pairs;

        void init() {
#if JCZ_V1_USE_64BIT_BANDS && (defined(WIN64) || defined(_WIN64) || defined(_M_X64) \
 || defined(_M_AMD64) || defined(_M_IA64) || defined(__amd64__) || defined(__x86_64__))
            // this->candidates[num].set();
            uint64_t * p = (uint64_t *)&this->candidates[0];
            uint64_t * end = (uint64_t *)&this->candidates[Numbers - 1];
            do {
                *p++ = kBitSet27_Double64;
                *p++ = kBitSet27_Double64;
                *p++ = kBitSet27_Double64;
            } while (p < end);

            *p++ = kBitSet27_Double64;
            *(uint32_t *)p = kBitSet27;

            // this->prevCandidates[num].reset();
            p = (uint64_t *)&this->prevCandidates[0];
            end = (uint64_t *)&this->prevCandidates[Numbers - 1];
            do {
                *p++ = 0;
                *p++ = 0;
                *p++ = 0;
            } while (p < end);

            *p++ = 0;
            *(uint32_t *)p = 0;

            // this->solvedCells.reset();
            // this->solvedRows.reset();
            p = (uint64_t *)&this->solvedCells;
            *p++ = 0;
            *p++ = 0;
            *p++ = 0;
            // this->pairs.reset();
            *p++ = 0;
            *(uint32_t *)p = 0;
#else
            for (size_t num = 0; num < Numbers; num++) {
                this->candidates[num].set();
                this->prevCandidates[num].reset();
            }
            this->solvedCells.reset();
            this->solvedRows.reset();
            this->pairs.reset();
#endif
        }
    };

    struct alignas(32) StaticData {
        PackedBitSet3D<BoardSize, Rows16, Cols16>   num_row_mask;
        PackedBitSet3D<BoardSize, Rows16, Cols16>   row_fill_mask;

        BandBoard flip_mask[BoardSize];
        BandBoard fill_mask[BoardSize];
        
        bool mask_is_inited;

        StaticData() : mask_is_inited(false) {
            if (!Static.mask_is_inited) {
                SudokuTable::initialize();
                this_type::init_mask();
                Static.mask_is_inited = true;
            }
        }
    };

#pragma pack(pop)

    int numSolutions_;
    int limitSolutions_;

    State states_[BoardSize];

    static StaticData Static;

public:
    Solver() : basic_solver(), numSolutions_(0), limitSolutions_(1) {
    }
    ~Solver() {}

private:
    static void make_flip_mask(size_t fill_pos, size_t row, size_t col) {
        PackedBitSet2D<Rows16, Cols16> & rows_mask = Static.num_row_mask[fill_pos];

        const CellInfo * pCellInfo = SudokuTable::cell_info;
        size_t box = pCellInfo[fill_pos].box;
        size_t cell = pCellInfo[fill_pos].cell;

        Static.row_fill_mask[fill_pos][row].set(col);

        size_t box_x = col / BoxCellsX;
        size_t box_y = row / BoxCellsY;
        size_t box_first_y = box_y * BoxCellsY;
        size_t box_first_x = box_x * BoxCellsX;

        // Literal::NumRowCols
        {
            size_t index = 0;
            // horizontal scanning
            for (size_t x = 0; x < Cols; x++) {
                rows_mask[row].set(x);
                index++;
            }
            // vertical scanning
            for (size_t y = 0; y < Rows; y++) {
                if (y != row) {
                    rows_mask[y].set(col);
                    index++;
                }
            }
            // scanning the current box
            for (size_t cy = 0; cy < BoxCellsY; cy++) {
                size_t row_y = box_first_y + cy;
                for (size_t cx = 0; cx < BoxCellsX; cx++) {
                    size_t col_x = box_first_x + cx;
                    rows_mask[row_y].set(col_x);
                    index++;
                }
            }
            assert(index == (Cols + Rows + (BoxCellsY * BoxCellsX) - 1));

            rows_mask[row].reset(col);
        }
    }

    static void transform_to_BandBoard(const PackedBitSet2D<Rows16, Cols16> & bit_mask,
                                       BandBoard & band_mask) {
        static const uint32_t kBoxCountY32 = (uint32_t)BoxCountY;
        static const uint32_t kBoxCellsY32 = (uint32_t)BoxCellsY;
        uint32_t band;
        for (band = 0; band < kBoxCountY32; band++) {
            uint32_t band_bits = 0;
            uint32_t row = band * kBoxCellsY32;
            for (uint32_t cellY = 0; cellY < kBoxCellsY32; cellY++) {
                uint32_t mask = bit_mask[row].value();
                row++;
                band_bits |= mask << (cellY * 9);
            }
            band_mask.bands[band] = band_bits;
        }
    }

    static void print_rowHiddenSingleMaskTbl() {
        printf("\n");
        printf("static const uint32_t rowHiddenSingleMaskTbl[512] = {\n");
        for (size_t i = 0; i < 512; i++) {
            if ((i % 32) == 0)
                printf("    ");
            uint32_t rowHiddenSingleMask = rowHiddenSingleReverseMaskTbl[i] ^ 0x07U;
            printf("%d", rowHiddenSingleMask);
            if ((i % 32) == 31)
                printf(",\n");
            else
                printf(", ");
        }
        printf("};\n");
        printf("\n");
    }

    static void init_flip_mask() {
        Static.num_row_mask.reset();
        Static.row_fill_mask.reset();

        size_t fill_pos = 0;
        for (size_t row = 0; row < Rows; row++) {
            for (size_t col = 0; col < Cols; col++) {
                make_flip_mask(fill_pos, row, col);
                transform_to_BandBoard(Static.num_row_mask[fill_pos], Static.flip_mask[fill_pos]);
                transform_to_BandBoard(Static.row_fill_mask[fill_pos], Static.fill_mask[fill_pos]);
                fill_pos++;
            }
        }
    }

    static void init_mask() {
        if (bPrintSudokuStaticInit) {
            printf("JCZ::v1::Solver::StaticData::init_mask()\n");
        }

        init_flip_mask();

        //print_rowHiddenSingleMaskTbl();
    }

    void extract_solution(State * state, Board & board) {
        assert(state != nullptr);
#if 1
#if defined(_DEBUG)
        for (size_t pos = 0; pos < BoardSize; pos++) {
            board.cells[pos] = '.';
        }
#endif

#if JCZ_V1_USE_64BIT_BANDS && (defined(WIN64) || defined(_WIN64) || defined(_M_X64) \
 || defined(_M_AMD64) || defined(_M_IA64) || defined(__amd64__) || defined(__x86_64__))
        for (size_t num = 0; num < Numbers; num++) {
            // Band64: 0
            {
                uint64_t band_bits = state->candidates[num].bands64;
                while (band_bits != 0) {
                    uint32_t bit_pos = BitUtils::bsf64(band_bits);
                    uint64_t mask = BitUtils::ls1b64(band_bits);
                    band_bits ^= mask;

                    size_t pos = bandBitPosToPos64[0][bit_pos];
                    assert(pos != size_t(-1));

                    assert(board.cells[pos] == '.');
                    board.cells[pos] = (char)('1' + num);
                }
            }

            // Band64: 1
            {
                uint64_t band_bits = state->candidates[num].bands32;
                while (band_bits != 0) {
                    uint32_t bit_pos = BitUtils::bsf64(band_bits);
                    uint64_t mask = BitUtils::ls1b64(band_bits);
                    band_bits ^= mask;

                    size_t pos = bandBitPosToPos64[1][bit_pos];
                    assert(pos != size_t(-1));

                    assert(board.cells[pos] == '.');
                    board.cells[pos] = (char)('1' + num);
                }
            }
        }
#else
        for (size_t num = 0; num < Numbers; num++) {
            // Band 0
            {
                uint32_t band_bits = state->candidates[num].bands[0];
                while (band_bits != 0) {
                    uint32_t bit_pos = BitUtils::bsf32(band_bits);
                    uint32_t mask = BitUtils::ls1b32(band_bits);
                    band_bits ^= mask;

                    size_t pos = bandBitPosToPos32[0][bit_pos];
                    assert(pos != size_t(-1));

                    assert(board.cells[pos] == '.');
                    board.cells[pos] = (char)('1' + num);
                }
            }

            // Band 1
            {
                uint32_t band_bits = state->candidates[num].bands[1];
                while (band_bits != 0) {
                    uint32_t bit_pos = BitUtils::bsf32(band_bits);
                    uint32_t mask = BitUtils::ls1b32(band_bits);
                    band_bits ^= mask;

                    size_t pos = bandBitPosToPos32[1][bit_pos];
                    assert(pos != size_t(-1));

                    assert(board.cells[pos] == '.');
                    board.cells[pos] = (char)('1' + num);
                }
            }

            // Band 2
            {
                uint32_t band_bits = state->candidates[num].bands[2];
                while (band_bits != 0) {
                    uint32_t bit_pos = BitUtils::bsf32(band_bits);
                    uint32_t mask = BitUtils::ls1b32(band_bits);
                    band_bits ^= mask;

                    size_t pos = bandBitPosToPos32[2][bit_pos];
                    assert(pos != size_t(-1));

                    assert(board.cells[pos] == '.');
                    board.cells[pos] = (char)('1' + num);
                }
            }
        }
#endif
#else
        for (size_t pos = 0; pos < BoardSize; pos++) {
            uint32_t mask = tables.posToMask[pos];
            uint32_t band = tables.div27[pos];
            for (size_t num = 0; num < Numbers; num++) {
                if ((state->candidates[num].bands[band] & mask) != 0) {
                    board.cells[pos] = (char)('1' + num);
                    break;
                }
            }
        }
#endif
    }

    JSTD_FORCED_INLINE
    int init_board(State * state, const Board & board) {
        state->init();

        int candidates = 0;
        for (size_t pos = 0; pos < BoardSize; pos++) {
            unsigned char val = board.cells[pos];
            if (val != '.') {
                size_t num = val - '1';
                assert(num >= (Sudoku::MinNumber - 1) && num <= (Sudoku::MaxNumber - 1));
                int validity = this->update_peer_cells_init(state, pos, num);
                if (validity == Status::Invalid)
                    return -1;
                candidates++;
            }
        }

        return candidates;
    }

    inline int update_peer_cells_init(State * state, size_t fill_pos, size_t fill_num) {
        assert(fill_pos < Sudoku::BoardSize);
        assert(fill_num >= (Sudoku::MinNumber - 1) && fill_num <= (Sudoku::MaxNumber - 1));

        uint32_t band = tables.div27[fill_pos];
        assert(band < 3);
        uint32_t mask = tables.posToMask[fill_pos];
        uint32_t verify_bits = state->candidates[fill_num].bands[band] & mask;
        if (verify_bits != 0) {
            state->candidates[fill_num].bands[band] &= bandUnsolvedMaskTbl32[fill_pos];
            uint32_t bandOtherUnsolvedMask = bandOtherUnsolvedMaskTbl[fill_pos];
            uint32_t peer1 = bandPeer1Tbl[band];
            uint32_t peer2 = bandPeer2Tbl[band];
            state->candidates[fill_num].bands[peer1] &= bandOtherUnsolvedMask;
            state->candidates[fill_num].bands[peer2] &= bandOtherUnsolvedMask;

            state->solvedCells.bands[band] |= mask;

            size_t rowBit = fill_num * Rows + tables.div9[fill_pos];
            uint32_t rowBand = tables.div27[rowBit];
            uint32_t shift = tables.mod27[rowBit];
            state->solvedRows.bands[rowBand] |= 1U << shift;

            uint32_t unsolved_bits = ~mask;
#if 0
            for (size_t num = 0; num < Numbers; num++) {
                state->candidates[num].bands[band] &= unsolved_bits;
            }
#else
            state->candidates[0].bands[band] &= unsolved_bits;
            state->candidates[1].bands[band] &= unsolved_bits;
            state->candidates[2].bands[band] &= unsolved_bits;
            state->candidates[3].bands[band] &= unsolved_bits;
            state->candidates[4].bands[band] &= unsolved_bits;
            state->candidates[5].bands[band] &= unsolved_bits;
            state->candidates[6].bands[band] &= unsolved_bits;
            state->candidates[7].bands[band] &= unsolved_bits;
            state->candidates[8].bands[band] &= unsolved_bits;
#endif
            state->candidates[fill_num].bands[band] |= mask;

            return Status::Success;
        }
        else {
            return Status::Invalid;
        }
    }

    inline void update_peer_cells(State * state, size_t fill_pos, size_t fill_num) {
        assert(fill_pos < Sudoku::BoardSize);
        assert(fill_num >= (Sudoku::MinNumber - 1) && fill_num <= (Sudoku::MaxNumber - 1));

        uint32_t band = tables.div27[fill_pos];
        uint32_t mask = tables.posToMask[fill_pos];
        uint32_t verify_bits = state->candidates[fill_num].bands[band] & mask;
        assert(verify_bits != 0);

        state->candidates[fill_num].bands[band] &= bandUnsolvedMaskTbl32[fill_pos];
        uint32_t bandOtherUnsolvedMask = bandOtherUnsolvedMaskTbl[fill_pos];
        int peer1 = bandPeer1Tbl[band];
        int peer2 = bandPeer2Tbl[band];
        state->candidates[fill_num].bands[peer1] &= bandOtherUnsolvedMask;
        state->candidates[fill_num].bands[peer2] &= bandOtherUnsolvedMask;

        state->solvedCells.bands[band] |= mask;

        size_t rowBit = fill_num * Rows + tables.div9[fill_pos];
        uint32_t rowBand = tables.div27[rowBit];
        uint32_t shift = tables.mod27[rowBit];
        state->solvedRows.bands[rowBand] |= 1U << shift;

        uint32_t unsolved_bits = ~mask;
#if 0
        for (size_t num = 0; num < Numbers; num++) {
            state->candidates[num].bands[band] &= unsolved_bits;
        }
#else
        state->candidates[0].bands[band] &= unsolved_bits;
        state->candidates[1].bands[band] &= unsolved_bits;
        state->candidates[2].bands[band] &= unsolved_bits;
        state->candidates[3].bands[band] &= unsolved_bits;
        state->candidates[4].bands[band] &= unsolved_bits;
        state->candidates[5].bands[band] &= unsolved_bits;
        state->candidates[6].bands[band] &= unsolved_bits;
        state->candidates[7].bands[band] &= unsolved_bits;
        state->candidates[8].bands[band] &= unsolved_bits;
#endif
        state->candidates[fill_num].bands[band] |= mask;
    }

    JSTD_FORCED_INLINE
    void update_band_solved_mask32(State * state, size_t band, size_t pos, size_t num) {
        state->candidates[num].bands[band] &= bandUnsolvedMaskTbl32[pos];
    }

    template <size_t band>
    JSTD_FORCED_INLINE
    void update_band_solved_mask64(State * state, size_t pos, size_t num) {
        if (band == 0)
            state->candidates[num].bands64 &= bandUnsolvedMaskTbl64[pos];
        else
            state->candidates[num].bands32 &= bandUnsolvedMaskTbl32[pos];
    }

    JSTD_FORCED_INLINE
    void update_band_solved_mask32(State * state, size_t band, size_t pos, size_t num, uint32_t mask) {
        if ((state->candidates[num].bands[band] & mask) != 0) {
            state->candidates[num].bands[band] &= bandUnsolvedMaskTbl32[pos];
        }
    }

    template <size_t band>
    JSTD_FORCED_INLINE
    void update_band_solved_mask64(State * state, size_t pos, size_t num, uint64_t mask) {
        if (band == 0) {
            if ((state->candidates[num].bands64 & mask) != 0) {
                state->candidates[num].bands64 &= bandUnsolvedMaskTbl64[pos];
            }
        }
        else {
            if ((state->candidates[num].bands32 & (uint32_t)mask) != 0) {
                state->candidates[num].bands32 &= bandUnsolvedMaskTbl32[pos];
            }
        }
    }

    template <uint32_t digit, uint32_t self, uint32_t peer1, uint32_t peer2, bool fast_mode>
    JSTD_FORCED_INLINE
    uint32_t update_up_down_cells(State * state, uint32_t & band) {
        uint32_t rowTriadsMask = rowTriadsMaskTbl[band & kFullRowBits] |
                                (rowTriadsMaskTbl[(band >> 9U) & kFullRowBits] << 3U) |
                                (rowTriadsMaskTbl[(band >> 18U) & kFullRowBits] << 6U);
        uint32_t lockedCandidates = keepLockedCandidatesTbl[rowTriadsMask];
        band &= lockedCandidates;
        if (fast_mode || band != 0) {
            assert(band != 0);
            uint32_t colCombBits = (band | (band >> 9U) | (band >> 18U)) & kFullRowBits;

            uint32_t colLockedSingleMask = colLockedSingleMaskTbl[colCombBits];
            state->candidates[digit].bands[peer1] &= colLockedSingleMask;
            state->candidates[digit].bands[peer2] &= colLockedSingleMask;

            state->candidates[digit].bands[self] = band;
            state->prevCandidates[digit].bands[self] = band;
            uint32_t newSolvedRows = rowHiddenSingleMaskTbl[rowTriadsSingleMaskTbl[rowTriadsMask] &
                                                            combColumnSingleMaskTbl[colCombBits]];
            return newSolvedRows;
        }
        else {
            return (uint32_t)-1;
        }
    }

    template <uint32_t digit, uint32_t self>
    JSTD_FORCED_INLINE
    void update_solved_rows(State * state, uint32_t band, uint32_t bandSolvedRows) {
        uint32_t solvedCells = band & solvedRowsBitMaskTbl[bandSolvedRows];
        //assert(solvedCells != 0);
        state->solvedCells.bands[self] |= solvedCells;
        uint32_t unsolvedCells = ~solvedCells;
        if (digit != 0)
            state->candidates[0].bands[self] &= unsolvedCells;
        if (digit != 1)
            state->candidates[1].bands[self] &= unsolvedCells;
        if (digit != 2)
            state->candidates[2].bands[self] &= unsolvedCells;
        if (digit != 3)
            state->candidates[3].bands[self] &= unsolvedCells;
        if (digit != 4)
            state->candidates[4].bands[self] &= unsolvedCells;
        if (digit != 5)
            state->candidates[5].bands[self] &= unsolvedCells;
        if (digit != 6)
            state->candidates[6].bands[self] &= unsolvedCells;
        if (digit != 7)
            state->candidates[7].bands[self] &= unsolvedCells;
        if (digit != 8)
            state->candidates[8].bands[self] &= unsolvedCells;
    }

    template <bool fast_mode = false>
    JSTD_NO_INLINE
    int find_hidden_singles(State * state) {
        register uint32_t solvedRows;
        register uint32_t bandSolvedRows;

        do {
            bandSolvedRows = (uint32_t)-1;

            /********* Number 1-3 Start *********/
        
            // Number 1
            solvedRows = state->solvedRows.bands[0];        
            if (!kCheckSolvedRows || (solvedRows & kFullRowBits) != kFullRowBits)
            {
                static const uint32_t digit = 0;

                // Number 1 - band 0
                register uint32_t band = state->candidates[digit].bands[0];
                if (band != state->prevCandidates[digit].bands[0]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 0, 1, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 0U;
                    if ((solvedRows & (0x007U << 0U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 0>(state, band, bandSolvedRows);
                    }
                }

                // Number 1 - band 1
                band = state->candidates[digit].bands[1];
                if (band != state->prevCandidates[digit].bands[1]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 1, 0, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 3U;
                    if ((solvedRows & (0x007U << 3U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 1>(state, band, bandSolvedRows);
                    }
                }

                // Number 1 - band 2
                band = state->candidates[digit].bands[2];
                if (band != state->prevCandidates[digit].bands[2]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 2, 0, 1, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 6U;
                    if ((solvedRows & (0x007U << 6U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 2>(state, band, bandSolvedRows);
                    }
                }
            }

            // Number 2
            if (!kCheckSolvedRows || (solvedRows & kFullRowBits_1) != kFullRowBits_1)
            {
                static const uint32_t digit = 1;

                // Number 2 - band 0
                register uint32_t band = state->candidates[digit].bands[0];
                if (band != state->prevCandidates[digit].bands[0]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 0, 1, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 9U;
                    if ((solvedRows & (0x007U << 9U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 0>(state, band, bandSolvedRows);
                    }
                }

                // Number 2 - band 1
                band = state->candidates[digit].bands[1];
                if (band != state->prevCandidates[digit].bands[1]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 1, 0, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 12U;
                    if ((solvedRows & (0x007U << 12U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 1>(state, band, bandSolvedRows);
                    }
                }

                // Number 2 - band 2
                band = state->candidates[digit].bands[2];
                if (band != state->prevCandidates[digit].bands[2]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 2, 0, 1, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 15U;
                    if ((solvedRows & (0x007U << 15U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 2>(state, band, bandSolvedRows);
                    }
                }
            }

            // Number 3
            if (!kCheckSolvedRows || (solvedRows & kFullRowBits_2) != kFullRowBits_2)
            {
                static const uint32_t digit = 2;

                // Number 3 - band 0
                register uint32_t band = state->candidates[digit].bands[0];
                if (band != state->prevCandidates[digit].bands[0]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 0, 1, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 18U;
                    if ((solvedRows & (0x007U << 18U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 0>(state, band, bandSolvedRows);
                    }
                }

                // Number 3 - band 1
                band = state->candidates[digit].bands[1];
                if (band != state->prevCandidates[digit].bands[1]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 1, 0, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 21U;
                    if ((solvedRows & (0x007U << 21U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 1>(state, band, bandSolvedRows);
                    }
                }

                // Number 3 - band 2
                band = state->candidates[digit].bands[2];
                if (band != state->prevCandidates[digit].bands[2]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 2, 0, 1, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 24U;
                    if ((solvedRows & (0x007U << 24U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 2>(state, band, bandSolvedRows);
                    }
                }
            }

            state->solvedRows.bands[0] = solvedRows;

            /********* Number 1-3 End *********/

            /********* Number 4-6 Start *********/
        
            // Number 4
            solvedRows = state->solvedRows.bands[1];        
            if (!kCheckSolvedRows || (solvedRows & kFullRowBits) != kFullRowBits)
            {
                static const uint32_t digit = 3;

                // Number 4 - band 0
                register uint32_t band = state->candidates[digit].bands[0];
                if (band != state->prevCandidates[digit].bands[0]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 0, 1, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 0U;
                    if ((solvedRows & (0x007U << 0U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 0>(state, band, bandSolvedRows);
                    }
                }

                // Number 4 - band 1
                band = state->candidates[digit].bands[1];
                if (band != state->prevCandidates[digit].bands[1]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 1, 0, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 3U;
                    if ((solvedRows & (0x007U << 3U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 1>(state, band, bandSolvedRows);
                    }
                }

                // Number 4 - band 2
                band = state->candidates[digit].bands[2];
                if (band != state->prevCandidates[digit].bands[2]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 2, 0, 1, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 6U;
                    if ((solvedRows & (0x007U << 6U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 2>(state, band, bandSolvedRows);
                    }
                }
            }

            // Number 5
            if (!kCheckSolvedRows || (solvedRows & kFullRowBits_1) != kFullRowBits_1)
            {
                static const uint32_t digit = 4;

                // Number 5 - band 0
                register uint32_t band = state->candidates[digit].bands[0];
                if (band != state->prevCandidates[digit].bands[0]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 0, 1, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 9U;
                    if ((solvedRows & (0x007U << 9U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 0>(state, band, bandSolvedRows);
                    }
                }

                // Number 5 - band 1
                band = state->candidates[digit].bands[1];
                if (band != state->prevCandidates[digit].bands[1]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 1, 0, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 12U;
                    if ((solvedRows & (0x007U << 12U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 1>(state, band, bandSolvedRows);
                    }
                }

                // Number 5 - band 2
                band = state->candidates[digit].bands[2];
                if (band != state->prevCandidates[digit].bands[2]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 2, 0, 1, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 15U;
                    if ((solvedRows & (0x007U << 15U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 2>(state, band, bandSolvedRows);
                    }
                }
            }

            // Number 6
            if (!kCheckSolvedRows || (solvedRows & kFullRowBits_2) != kFullRowBits_2)
            {
                static const uint32_t digit = 5;

                // Number 6 - band 0
                register uint32_t band = state->candidates[digit].bands[0];
                if (band != state->prevCandidates[digit].bands[0]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 0, 1, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 18U;
                    if ((solvedRows & (0x007U << 18U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 0>(state, band, bandSolvedRows);
                    }
                }

                // Number 6 - band 1
                band = state->candidates[digit].bands[1];
                if (band != state->prevCandidates[digit].bands[1]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 1, 0, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 21U;
                    if ((solvedRows & (0x007U << 21U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 1>(state, band, bandSolvedRows);
                    }
                }

                // Number 6 - band 2
                band = state->candidates[digit].bands[2];
                if (band != state->prevCandidates[digit].bands[2]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 2, 0, 1, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 24U;
                    if ((solvedRows & (0x007U << 24U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 2>(state, band, bandSolvedRows);
                    }
                }
            }

            state->solvedRows.bands[1] = solvedRows;

            /********* Number 4-6 End *********/

            /********* Number 7-9 Start *********/
        
            // Number 7
            solvedRows = state->solvedRows.bands[2];        
            if (!kCheckSolvedRows || (solvedRows & kFullRowBits) != kFullRowBits)
            {
                static const uint32_t digit = 6;

                // Number 7 - band 0
                register uint32_t band = state->candidates[digit].bands[0];
                if (band != state->prevCandidates[digit].bands[0]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 0, 1, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 0U;
                    if ((solvedRows & (0x007U << 0U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 0>(state, band, bandSolvedRows);
                    }
                }

                // Number 7 - band 1
                band = state->candidates[digit].bands[1];
                if (band != state->prevCandidates[digit].bands[1]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 1, 0, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 3U;
                    if ((solvedRows & (0x007U << 3U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 1>(state, band, bandSolvedRows);
                    }
                }

                // Number 7 - band 2
                band = state->candidates[digit].bands[2];
                if (band != state->prevCandidates[digit].bands[2]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 2, 0, 1, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 6U;
                    if ((solvedRows & (0x007U << 6U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 2>(state, band, bandSolvedRows);
                    }
                }
            }

            // Number 8
            if (!kCheckSolvedRows || (solvedRows & kFullRowBits_1) != kFullRowBits_1)
            {
                static const uint32_t digit = 7;

                // Number 8 - band 0
                register uint32_t band = state->candidates[digit].bands[0];
                if (band != state->prevCandidates[digit].bands[0]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 0, 1, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 9U;
                    if ((solvedRows & (0x007U << 9U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 0>(state, band, bandSolvedRows);
                    }
                }

                // Number 8 - band 1
                band = state->candidates[digit].bands[1];
                if (band != state->prevCandidates[digit].bands[1]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 1, 0, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 12U;
                    if ((solvedRows & (0x007U << 12U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 1>(state, band, bandSolvedRows);
                    }
                }

                // Number 8 - band 2
                band = state->candidates[digit].bands[2];
                if (band != state->prevCandidates[digit].bands[2]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 2, 0, 1, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 15U;
                    if ((solvedRows & (0x007U << 15U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 2>(state, band, bandSolvedRows);
                    }
                }
            }

            // Number 9
            if (!kCheckSolvedRows || (solvedRows & kFullRowBits_2) != kFullRowBits_2)
            {
                static const uint32_t digit = 8;

                // Number 9 - band 0
                register uint32_t band = state->candidates[digit].bands[0];
                if (band != state->prevCandidates[digit].bands[0]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 0, 1, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 18U;
                    if ((solvedRows & (0x007U << 18U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 0>(state, band, bandSolvedRows);
                    }
                }

                // Number 9 - band 1
                band = state->candidates[digit].bands[1];
                if (band != state->prevCandidates[digit].bands[1]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 1, 0, 2, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 21U;
                    if ((solvedRows & (0x007U << 21U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 1>(state, band, bandSolvedRows);
                    }
                }

                // Number 9 - band 2
                band = state->candidates[digit].bands[2];
                if (band != state->prevCandidates[digit].bands[2]) {
                    bandSolvedRows = this->update_up_down_cells<digit, 2, 0, 1, fast_mode>(state, band);
                    if (!fast_mode && (bandSolvedRows == (uint32_t)-1))
                        return Status::Invalid;
                    uint32_t newSolvedRows = bandSolvedRows << 24U;
                    if ((solvedRows & (0x007U << 24U)) != newSolvedRows) {
                        solvedRows |= newSolvedRows;
                        this->update_solved_rows<digit, 2>(state, band, bandSolvedRows);
                    }
                }
            }

            state->solvedRows.bands[2] = solvedRows;

            /********* Number 7-9 End *********/
        } while (bandSolvedRows != uint32_t(-1));

        return Status::Success;
    }

    JSTD_NO_INLINE
    int fast_find_naked_singles(State * state) {
        register int cell_count = 0;
#if JCZ_V1_USE_64BIT_BANDS && (defined(WIN64) || defined(_WIN64) || defined(_M_X64) \
 || defined(_M_AMD64) || defined(_M_IA64) || defined(__amd64__) || defined(__x86_64__))
        // Bands64: 0
        {
            register uint64_t R1, R2;
            register uint64_t band_bits;
#if 0
            R1 = state->candidates[0].bands64;
            R2 = 0;

            for (size_t num = 1; num < Numbers; num++) {
                band_bits = state->candidates[num].bands64;
                R2 |= R1 & band_bits;
                R1 |= band_bits;
            }
#else
            R1 = state->candidates[0].bands64;

            band_bits = state->candidates[1].bands64;
            R2 = R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[2].bands64;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[3].bands64;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[4].bands64;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[5].bands64;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[6].bands64;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[7].bands64;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[8].bands64;
            R2 |= R1 & band_bits;
            R1 |= band_bits;
#endif
            assert(R1 == kBitSet27_Double64);
            //if (R1 != kBitSet27_Double64) return -1;

            uint64_t solved_bits = state->solvedCells.bands64;
            R1 &= ~R2;
            R1 &= ~solved_bits;

            while (R1 != 0) {
                size_t bit_pos = BitUtils::bsf64(R1);
                uint64_t bit = BitUtils::ls1b64(R1);
                R1 ^= bit;

                size_t pos = bandBitPosToPos64[0][bit_pos];
                assert(pos != size_t(-1));

                for (size_t num = 0; num < Numbers; num++) {
                    uint64_t band_bits = state->candidates[num].bands64;
                    if ((band_bits & bit) != 0) {
                        this->update_band_solved_mask64<0>(state, pos, num);
                        cell_count++;
                        break;
                    }
                }
            }
        }

        // Bands64: 1
        {
            register uint32_t R1, R2;
            register uint32_t band_bits;
#if 0
            R1 = state->candidates[0].bands32;
            R2 = 0;

            for (size_t num = 1; num < Numbers; num++) {
                band_bits = state->candidates[num].bands32;
                R2 |= R1 & band_bits;
                R1 |= band_bits;
            }
#else
            R1 = state->candidates[0].bands32;

            band_bits = state->candidates[1].bands32;
            R2 = R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[2].bands32;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[3].bands32;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[4].bands32;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[5].bands32;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[6].bands32;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[7].bands32;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[8].bands32;
            R2 |= R1 & band_bits;
            R1 |= band_bits;
#endif
            assert(R1 == kBitSet27);
            //if (R1 != kBitSet27) return -1;

            uint32_t solved_bits = state->solvedCells.bands32;
            R1 &= ~R2;
            R1 &= ~solved_bits;

            while (R1 != 0) {
                size_t bit_pos = BitUtils::bsf32(R1);
                uint32_t bit = BitUtils::ls1b32(R1);
                R1 ^= bit;

                size_t pos = bandBitPosToPos32[2][bit_pos];
                assert(pos != size_t(-1));

                for (size_t num = 0; num < Numbers; num++) {
                    uint32_t band_bits = state->candidates[num].bands32;
                    if ((band_bits & bit) != 0) {
                        this->update_band_solved_mask32(state, 2, pos, num);
                        cell_count++;
                        break;
                    }
                }
            }
        }
#else // !__amd64__
        for (int band = 0; band < 3; band++) {
            register uint32_t R1, R2;
            register uint32_t band_bits;
#if 0
            R1 = state->candidates[0].bands[band];
            R2 = 0;

            for (size_t num = 1; num < Numbers; num++) {
                band_bits = state->candidates[num].bands[band];
                R2 |= R1 & band_bits;
                R1 |= band_bits;
            }
#else
            R1 = state->candidates[0].bands[band];

            band_bits = state->candidates[1].bands[band];
            R2 = R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[2].bands[band];
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[3].bands[band];
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[4].bands[band];
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[5].bands[band];
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[6].bands[band];
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[7].bands[band];
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[8].bands[band];
            R2 |= R1 & band_bits;
            R1 |= band_bits;
#endif
            assert(R1 == kBitSet27);
            //if (R1 != kBitSet27) return -1;

            uint32_t solved_bits = state->solvedCells.bands[band];
            R1 &= ~R2;
            R1 &= ~solved_bits;

            while (R1 != 0) {
                size_t bit_pos = BitUtils::bsf32(R1);
                uint32_t bit = BitUtils::ls1b32(R1);
                R1 ^= bit;

                size_t pos = bandBitPosToPos32[band][bit_pos];
                assert(pos != size_t(-1));

                for (size_t num = 0; num < Numbers; num++) {
                    uint32_t band_bits = state->candidates[num].bands[band];
                    if ((band_bits & bit) != 0) {
                        this->update_band_solved_mask32(state, band, pos, num);
                        cell_count++;
                        break;
                    }
                }
            }
        }
#endif // __amd64__
        return cell_count;
    }

    JSTD_NO_INLINE
    void find_naked_singles_only_pair(State * state) {
#if JCZ_V1_USE_64BIT_BANDS && (defined(WIN64) || defined(_WIN64) || defined(_M_X64) \
 || defined(_M_AMD64) || defined(_M_IA64) || defined(__amd64__) || defined(__x86_64__))
        // Bands64: 0
        {
            register uint64_t R1, R2, R3;
            register uint64_t band_bits;
#if 0
            R1 = state->candidates[0].bands64;
            R2 = 0;
            R3 = 0;

            for (size_t num = 1; num < Numbers; num++) {
                band_bits = state->candidates[num].bands64;
                R3 |= R2 & band_bits;
                R2 |= R1 & band_bits;
                R1 |= band_bits;
            }
#else
            R1 = state->candidates[0].bands64;

            band_bits = state->candidates[1].bands64;
            R2 = R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[2].bands64;
            R3 = R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[3].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[4].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[5].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[6].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[7].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[8].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;
#endif
            assert(R1 == kBitSet27_Double64);

            uint64_t solved_bits = state->solvedCells.bands64;
            R1 &= ~R2;
            R2 &= ~R3;
            R1 &= ~solved_bits;

            state->pairs.bands64 = R2;

            assert(R1 == 0);
        }

        // Bands64: 1
        {
            register uint32_t R1, R2, R3;
            register uint32_t band_bits;
#if 0
            R1 = state->candidates[0].bands32;
            R2 = 0;
            R3 = 0;

            for (size_t num = 1; num < Numbers; num++) {
                band_bits = state->candidates[num].bands32;
                R3 |= R2 & band_bits;
                R2 |= R1 & band_bits;
                R1 |= band_bits;
            }
#else
            R1 = state->candidates[0].bands32;

            band_bits = state->candidates[1].bands32;
            R2 = R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[2].bands32;
            R3 = R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[3].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[4].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[5].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[6].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[7].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[8].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;
#endif
            assert(R1 == kBitSet27);

            uint32_t solved_bits = state->solvedCells.bands32;
            R1 &= ~R2;
            R2 &= ~R3;
            R1 &= ~solved_bits;

            state->pairs.bands32 = R2;

            assert(R1 == 0);
        }
#else // !__amd64__
        for (int band = 0; band < 3; band++) {
            register uint32_t R1, R2, R3;
            register uint32_t band_bits;
#if 0
            R1 = state->candidates[0].bands[band];
            R2 = 0;
            R3 = 0;

            for (size_t num = 1; num < Numbers; num++) {
                band_bits = state->candidates[num].bands[band];
                R3 |= R2 & band_bits;
                R2 |= R1 & band_bits;
                R1 |= band_bits;
            }
#else
            R1 = state->candidates[0].bands[band];

            band_bits = state->candidates[1].bands[band];
            R2 = R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[2].bands[band];
            R3 = R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[3].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[4].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[5].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[6].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[7].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[8].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;
#endif
            assert(R1 == kBitSet27);

            uint32_t solved_bits = state->solvedCells.bands[band];
            R1 &= ~R2;
            R2 &= ~R3;
            R1 &= ~solved_bits;

            state->pairs.bands[band] = R2;

            assert(R1 == 0);
        }
#endif // __amd64__
    }

    JSTD_NO_INLINE
    int normal_find_naked_singles(State * state) {
        register int cell_count = 0;
#if JCZ_V1_USE_64BIT_BANDS && (defined(WIN64) || defined(_WIN64) || defined(_M_X64) \
 || defined(_M_AMD64) || defined(_M_IA64) || defined(__amd64__) || defined(__x86_64__))
        // Bands64: 0
        {
            register uint64_t R1, R2, R3;
            register uint64_t band_bits;
#if 0
            R1 = state->candidates[0].bands64;
            R2 = 0;
            R3 = 0;

            for (size_t num = 1; num < Numbers; num++) {
                band_bits = state->candidates[num].bands64;
                R3 |= R2 & band_bits;
                R2 |= R1 & band_bits;
                R1 |= band_bits;
            }
#else
            R1 = state->candidates[0].bands64;

            band_bits = state->candidates[1].bands64;
            R2 = R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[2].bands64;
            R3 = R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[3].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[4].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[5].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[6].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[7].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[8].bands64;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;
#endif
            if (R1 != kBitSet27_Double64) return -1;

            uint64_t solved_bits = state->solvedCells.bands64;
            R1 &= ~R2;
            R2 &= ~R3;
            R1 &= ~solved_bits;

            state->pairs.bands64 = R2;

            while (R1 != 0) {
                size_t bit_pos = BitUtils::bsf64(R1);
                uint64_t bit = BitUtils::ls1b64(R1);
                R1 ^= bit;

                size_t pos = bandBitPosToPos64[0][bit_pos];
                assert(pos != size_t(-1));

                for (size_t num = 0; num < Numbers; num++) {
                    uint64_t band_bits = state->candidates[num].bands64;
                    if ((band_bits & bit) != 0) {
                        this->update_band_solved_mask64<0>(state, pos, num);
                        cell_count++;
                        break;
                    }
                }
            }
        }

        // Bands64: 1
        {
            register uint32_t R1, R2, R3;
            register uint32_t band_bits;
#if 0
            R1 = state->candidates[0].bands32;
            R2 = 0;
            R3 = 0;

            for (size_t num = 1; num < Numbers; num++) {
                band_bits = state->candidates[num].bands32;
                R3 |= R2 & band_bits;
                R2 |= R1 & band_bits;
                R1 |= band_bits;
            }
#else
            R1 = state->candidates[0].bands32;

            band_bits = state->candidates[1].bands32;
            R2 = R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[2].bands32;
            R3 = R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[3].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[4].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[5].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[6].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[7].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[8].bands32;
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;
#endif
            if (R1 != kBitSet27) return -1;

            uint32_t solved_bits = state->solvedCells.bands32;
            R1 &= ~R2;
            R2 &= ~R3;
            R1 &= ~solved_bits;

            state->pairs.bands32 = R2;

            while (R1 != 0) {
                size_t bit_pos = BitUtils::bsf32(R1);
                uint32_t bit = BitUtils::ls1b32(R1);
                R1 ^= bit;

                size_t pos = bandBitPosToPos32[2][bit_pos];
                assert(pos != size_t(-1));

                for (size_t num = 0; num < Numbers; num++) {
                    uint32_t band_bits = state->candidates[num].bands32;
                    if ((band_bits & bit) != 0) {
                        this->update_band_solved_mask32(state, 2, pos, num);
                        cell_count++;
                        break;
                    }
                }
            }
        }
#else // !__amd64__
        for (int band = 0; band < 3; band++) {
            register uint32_t R1, R2, R3;
            register uint32_t band_bits;
#if 0
            R1 = state->candidates[0].bands[band];
            R2 = 0;
            R3 = 0;

            for (size_t num = 1; num < Numbers; num++) {
                band_bits = state->candidates[num].bands[band];
                R3 |= R2 & band_bits;
                R2 |= R1 & band_bits;
                R1 |= band_bits;
            }
#else
            R1 = state->candidates[0].bands[band];

            band_bits = state->candidates[1].bands[band];
            R2 = R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[2].bands[band];
            R3 = R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[3].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[4].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[5].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[6].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[7].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;

            band_bits = state->candidates[8].bands[band];
            R3 |= R2 & band_bits;
            R2 |= R1 & band_bits;
            R1 |= band_bits;
#endif
            if (R1 != kBitSet27) return -1;

            uint32_t solved_bits = state->solvedCells.bands[band];
            R1 &= ~R2;
            R2 &= ~R3;
            R1 &= ~solved_bits;

            state->pairs.bands[band] = R2;

            while (R1 != 0) {
                size_t bit_pos = BitUtils::bsf32(R1);
                uint32_t bit = BitUtils::ls1b32(R1);
                R1 ^= bit;

                size_t pos = bandBitPosToPos32[band][bit_pos];
                assert(pos != size_t(-1));

                for (size_t num = 0; num < Numbers; num++) {
                    uint32_t band_bits = state->candidates[num].bands[band];
                    if ((band_bits & bit) != 0) {
                        this->update_band_solved_mask32(state, band, pos, num);
                        cell_count++;
                        break;
                    }
                }
            }
        }
#endif // __amd64__
        return cell_count;
    }

    JSTD_NO_INLINE
    int guess_bivalue_cells(State *& state, Board & board) {
        assert(state != nullptr);
#if JCZ_V1_USE_64BIT_BANDS && (defined(WIN64) || defined(_WIN64) || defined(_M_X64) \
 || defined(_M_AMD64) || defined(_M_IA64) || defined(__amd64__) || defined(__x86_64__))
        // Band64: 0
        {
            uint64_t pairs = state->pairs.bands64;
            if (pairs) {
                size_t bit_pos = BitUtils::bsf64(pairs);
                uint64_t mask = BitUtils::ls1b64(pairs);

                size_t pos = bandBitPosToPos64[0][bit_pos];
                assert(pos != size_t(-1));

                int tries = 2;
                for (size_t num = 0; num < Numbers; num++) {
                    if ((state->candidates[num].bands64 & mask) != 0) {
                        if (--tries) {
                            // First of pair
                            std::memcpy((void *)(state + 1), (const void *)state, sizeof(State));
                            state->candidates[num].bands64 ^= mask;
                            ++state;
                            basic_solver::num_guesses++;

                            this->update_band_solved_mask64<0>(state, pos, num);

                            if (this->find_all_single_literals<false>(state) != Status::Invalid) {
                                this->guess_next_cell(state, board);
                            }
                            --state;
                        }
                        else {
                            // Second of pair
                            this->update_band_solved_mask64<0>(state, pos, num);

                            if (this->find_all_single_literals<false>(state) != Status::Invalid) {
                                this->guess_next_cell(state, board);
                            }
                            return Status::Success;
                        }
                    }
                }
            }
        }

        // Band64: 1
        {
            uint32_t pairs = state->pairs.bands32;
            if (pairs) {
                size_t bit_pos = BitUtils::bsf32(pairs);
                uint32_t mask = BitUtils::ls1b32(pairs);

                size_t pos = bandBitPosToPos32[2][bit_pos];
                assert(pos != size_t(-1));

                int tries = 2;
                for (size_t num = 0; num < Numbers; num++) {
                    if ((state->candidates[num].bands32 & mask) != 0) {
                        if (--tries) {
                            // First of pair
                            std::memcpy((void *)(state + 1), (const void *)state, sizeof(State));
                            state->candidates[num].bands32 ^= mask;
                            ++state;
                            basic_solver::num_guesses++;

                            this->update_band_solved_mask32(state, 2, pos, num);

                            if (this->find_all_single_literals<false>(state) != Status::Invalid) {
                                this->guess_next_cell(state, board);
                            }
                            --state;
                        }
                        else {
                            // Second of pair
                            this->update_band_solved_mask32(state, 2, pos, num);

                            if (this->find_all_single_literals<false>(state) != Status::Invalid) {
                                this->guess_next_cell(state, board);
                            }
                            return Status::Success;
                        }
                    }
                }
            }
        }
#else // !__amd64__
        for (size_t band = 0; band < 3; band++) {
            uint32_t pairs = state->pairs.bands[band];
            if (pairs) {
                size_t bit_pos = BitUtils::bsf32(pairs);
                uint32_t mask = BitUtils::ls1b32(pairs);

                size_t pos = bandBitPosToPos32[band][bit_pos];
                assert(pos != size_t(-1));

                int tries = 2;
                for (size_t num = 0; num < Numbers; num++) {
                    if ((state->candidates[num].bands[band] & mask) != 0) {
                        if (--tries) {
                            // First of pair
                            std::memcpy((void *)(state + 1), (const void *)state, sizeof(State));
                            state->candidates[num].bands[band] ^= mask;
                            ++state;
                            basic_solver::num_guesses++;

                            this->update_band_solved_mask32(state, band, pos, num);

                            if (this->find_all_single_literals<false>(state) != Status::Invalid) {
                                this->guess_next_cell(state, board);
                            }
                            --state;
                        }
                        else {
                            // Second of pair
                            this->update_band_solved_mask32(state, band, pos, num);

                            if (this->find_all_single_literals<false>(state) != Status::Invalid) {
                                this->guess_next_cell(state, board);
                            }
                            return Status::Success;
                        }
                    }
                }
            }
        }
#endif // __amd64__
        return Status::Failed;
    }

    JSTD_NO_INLINE
    int guess_first_cell(State *& state, Board & board) {
        assert(state != nullptr);
#if JCZ_V1_USE_64BIT_BANDS && (defined(WIN64) || defined(_WIN64) || defined(_M_X64) \
 || defined(_M_AMD64) || defined(_M_IA64) || defined(__amd64__) || defined(__x86_64__))
        // Band64: 0
        uint64_t unsolvedCells = state->solvedCells.bands64 ^ kBitSet27_Double64;
        if (unsolvedCells != 0) {
            size_t bit_pos = BitUtils::bsf64(unsolvedCells);
            uint64_t mask = BitUtils::ls1b64(unsolvedCells);

            size_t pos = bandBitPosToPos64[0][bit_pos];
            assert(pos != size_t(-1));

            for (size_t num = 0; num < Numbers; num++) {
                if ((state->candidates[num].bands64 & mask) != 0) {
                    std::memcpy((void *)(state + 1), (const void *)state, sizeof(State));
                    state->candidates[num].bands64 ^= mask;
                    ++state;
                    basic_solver::num_guesses++;

                    this->update_band_solved_mask64<0>(state, pos, num);

                    if (this->find_all_single_literals<false>(state) != Status::Invalid) {
                        this->guess_next_cell(state, board);
                    }
                    --state;
                }
            }

            return Status::Success;
        }

        uint32_t unsolvedCells32 = state->solvedCells.bands32 ^ kBitSet27;
        if (unsolvedCells32 != 0) {
            size_t bit_pos = BitUtils::bsf32(unsolvedCells32);
            uint32_t mask = BitUtils::ls1b32(unsolvedCells32);

            size_t pos = bandBitPosToPos32[2][bit_pos];
            assert(pos != size_t(-1));

            for (size_t num = 0; num < Numbers; num++) {
                if ((state->candidates[num].bands32 & mask) != 0) {
                    std::memcpy((void *)(state + 1), (const void *)state, sizeof(State));
                    state->candidates[num].bands32 ^= mask;
                    ++state;
                    basic_solver::num_guesses++;

                    this->update_band_solved_mask32(state, 2, pos, num);

                    if (this->find_all_single_literals<false>(state) != Status::Invalid) {
                        this->guess_next_cell(state, board);
                    }
                    --state;
                }
            }

            return Status::Success;
        }
#else
        for (size_t band = 0; band < 3; band++) {
            uint32_t unsolvedCells = state->solvedCells.bands[band] ^ kBitSet27;
            if (unsolvedCells == 0)
                continue;

            size_t bit_pos = BitUtils::bsf32(unsolvedCells);
            uint32_t mask = BitUtils::ls1b32(unsolvedCells);

            size_t pos = bandBitPosToPos32[band][bit_pos];
            assert(pos != size_t(-1));

            for (size_t num = 0; num < Numbers; num++) {
                if ((state->candidates[num].bands[band] & mask) != 0) {
                    std::memcpy((void *)(state + 1), (const void *)state, sizeof(State));
                    state->candidates[num].bands[band] ^= mask;
                    ++state;
                    basic_solver::num_guesses++;

                    this->update_band_solved_mask32(state, band, pos, num);

                    if (this->find_all_single_literals<false>(state) != Status::Invalid) {
                        this->guess_next_cell(state, board);
                    }
                    --state;
                }
            }

            return Status::Success;
        }
#endif
        return Status::Failed;
    }

    template <bool NeedCheckSolved = true>
    JSTD_FORCED_INLINE
    int guess_next_cell(State *& state, Board & board) {
        assert(state != nullptr);
        if (NeedCheckSolved && this->is_solved(state)) {
            if (kSearchMode > SearchMode::OneSolution) {
                if (this->numSolutions_ == 0)
                    this->extract_solution(state, board);
                this->numSolutions_++;
                if (kSearchMode == SearchMode::MoreThanOneSolution) {
                    if (this->numSolutions_ > this->limitSolutions_)
                        return Status::Solved;
                }
                return Status::Solved;
            }
            else {
                if (this->numSolutions_ == 0)
                    this->extract_solution(state, board);
                this->numSolutions_++;
                return Status::Solved;
            }
        }

        if (this->guess_bivalue_cells(state, board) == Status::Failed) {
            this->guess_first_cell(state, board);
        }

        return Status::Success;
    }

    template <bool fast_mode>
    int find_naked_singles(State * state) {
        int naked_singles;
        if (fast_mode)
            naked_singles = this->fast_find_naked_singles(state);
        else
            naked_singles = this->normal_find_naked_singles(state);
        return naked_singles;
    }

    template <bool fast_mode>
    JSTD_FORCED_INLINE
    int find_all_single_literals(State * state) {
        if (!fast_mode && (this->numSolutions_ >= this->limitSolutions_))
            return Status::Invalid;

        do {
            int status = this->find_hidden_singles<fast_mode>(state);
            if (!fast_mode && (status == Status::Invalid))
                return status;

            if (this->is_solved(state)) {
                return Status::Solved;
            }

            int naked_singles = this->find_naked_singles<fast_mode>(state);
            if (naked_singles > 0)
                continue;
            else if (!fast_mode && (naked_singles < 0))
                return Status::Invalid;
            else
                break;
        } while (1);

        return Status::Success;
    }

    JSTD_FORCED_INLINE
    bool is_solved(State * state) {
#if JCZ_V1_USE_64BIT_BANDS && (defined(WIN64) || defined(_WIN64) || defined(_M_X64) \
 || defined(_M_AMD64) || defined(_M_IA64) || defined(__amd64__) || defined(__x86_64__))
        return ((state->solvedCells.bands64 == kBitSet27_Double64) &&
                (state->solvedCells.bands32 == kBitSet27));
#else
        return ((state->solvedCells.bands[0] == kBitSet27) &&
                (state->solvedCells.bands[1] == kBitSet27) &&
                (state->solvedCells.bands[2] == kBitSet27));
#endif
    }

public:
    JSTD_FORCED_INLINE
    int search(State *& state, Board & board) {
        int status;
        if (kUseFastMode) {
            this->find_naked_singles_only_pair(state);
        }
        status = this->guess_next_cell<kUseFastMode>(state, board);
        return status;
    }

    JSTD_NO_INLINE
    int solve(const Board & board, Board & solution, int limitSolutions = 1) {
        this->numSolutions_ = 0;
        this->limitSolutions_ = limitSolutions;
        basic_solver::num_guesses = 0;

        State * state = &this->states_[0];
        int candidates = this->init_board(state, board);
        if (candidates < (int)Sudoku::MinInitCandidates)
            return -1;

        int status = this->find_all_single_literals<kUseFastMode>(state);
        if (status == Status::Solved) {
            this->extract_solution(state, solution);
            return 1;
        }
        else if (!kUseFastMode && (status == Status::Invalid)) {
            return 0;
        }

#if JCZ_V1_ONLY_NO_GUESS
        return 0;
#else
        status = this->search(state, solution);
        return this->numSolutions_;
#endif
    }

    void display_result(Board & board, double elapsed_time,
                        bool print_answer = true,
                        bool print_all_answers = true) {
        basic_solver::display_result<kSearchMode>(board, elapsed_time, print_answer, print_all_answers);
    }
};

Solver::StaticData Solver::Static;

} // namespace v1
} // namespace JCZ
} // namespace gzSudoku

#endif // GZ_SUDOKU_JCZSOLVE_V1_H
